﻿using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Devonline.Identity;

public static class IdentityExtensions
{
    /// <summary>
    /// 将认证相关默认设置放入依赖注入容器
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddDefaultIdentity<TIdentityDbContext>(this IServiceCollection services) where TIdentityDbContext : DbContext
    {
        services.AddIdentity<User, Role>(options =>
        {
            // Password settings.
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequireUppercase = true;
            options.Password.RequiredLength = 8;
            options.Password.RequiredUniqueChars = 1;

            // Lockout settings.
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(1);
            options.Lockout.MaxFailedAccessAttempts = 5;
            options.Lockout.AllowedForNewUsers = true;

            // User settings.
            options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
            options.User.RequireUniqueEmail = false;
        })
            .AddEntityFrameworkStores<TIdentityDbContext>()
            .AddPersonalDataProtection<AesProtector, DefaultKeyRing>()
            .AddDefaultTokenProviders();

        services.TryAddScoped<DbContext, TIdentityDbContext>();
        services.TryAddScoped<UserStore>();
        services.TryAddScoped<RoleStore>();
        services.TryAddScoped<ResourceAccessStore>();
        return services;
    }

    /// <summary>
    /// 针对字符串类型主键的 IIdentity 实例获取对应的 IdentityType
    /// </summary>
    /// <param name="identity"></param>
    /// <returns></returns>
    public static IdentityType GetIdentityType(this IIdentity<string> identity) => identity.GetIdentityType<string>();

    /// <summary>
    /// 针对 IIdentity<TKey> 实例获取对应的 IdentityType
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <param name="identity"></param>
    /// <returns></returns>
    public static IdentityType GetIdentityType<TKey>(this IIdentity<TKey> identity) where TKey : IConvertible, IEquatable<TKey> => identity.GetIdentityType<IIdentity<TKey>, TKey>();

    /// <summary>
    /// 针对任意引用类型获取对应的身份 IdentityType
    /// </summary>
    /// <typeparam name="TIdentity"></typeparam>
    /// <param name="identity"></param>
    /// <returns></returns>
    public static IdentityType GetIdentityType<TIdentity, TKey>(this TIdentity identity) where TIdentity : class, IIdentity<TKey> where TKey : IConvertible, IEquatable<TKey> => identity switch
    {
        User<TKey> => IdentityType.User,
        Role<TKey> => IdentityType.Role,
        Group<TKey> => IdentityType.Group,
        Level<TKey> => IdentityType.Level,
        _ => IdentityType.System
    };
}